/* jshint esversion:6 */

/**
 * @author: errorcpp@qq.com
 */

require("./xlib_base");
require("./xlib_ArrayBufferUtility");

var xwindow = ("undefined" != typeof(window)) ? window : global;
xwindow.xlib = xwindow.xlib || {};
let xlib = xwindow.xlib;

/**
 * @brief: 兼容性更好的buffer类
 */
class ByteBuffer {
    constructor() {
        this._endian           = true;                    // 大小端，默认大端
        this._default_capacity = 512;                     // 默认容量 
        this._capacity         = this._default_capacity;  // 当前容量
        this._size             = 0;                       // 当前大小
        this._arrbuff          = new ArrayBuffer(this._capacity);  // 实际缓冲区
        this._dv               = new DataView(this._arrbuff);      // 辅助操作变量
        this._debug_dv         = new Uint8Array(this._arrbuff);    // 辅助调试变量
    }

    /**
     * @brief: 获取当前大小
     */
    Size() {
        return this._size;
    }

    /**
     * @brief: 调整大小：变大数据保留，尾部填零；变小数据保留，尾部截取
     * @param {number} new_size: 新的大小
     */
    Resize(new_size) {
        if(this._capacity < new_size) {
            // captcity按512整数倍增长
            let new_capacity = (parseInt(this._capacity / 512) + 1) * 512;
            this.ResetCapacity(new_capacity);
        }

        this._size = new_size;
    }

    /**
     * @brief: 重设容量：容量变小不做任何操作；容量变大
     * @param {number} new_capacity: 新的容量
     */
    ResetCapacity(new_capacity) {
        if(this._capacity >= new_capacity) return;

        let arrbuff_new = new ArrayBuffer(new_capacity);
        xlib.ArrayBuffer_DataCopy(this._arrbuff, 0, this._arrbuff.byteLength, arrbuff_new, 0);
        this._arrbuff  = arrbuff_new;
        this._dv       = new DataView(arrbuff_new);
        this._debug_dv = new Uint8Array(arrbuff_new);
        this._capacity =new_capacity;
    }
    
    /**
     * @brief: 读取uint8_t
     * @param {number} offset: 偏移字节数
     */
    ReadU8(offset) {
        if(offset === void 0)
            offset = 0;

        let read_len = 1;
        if(offset + read_len > this._size)
            throw("out of range");

        return this._dv.getUint8(offset);
    }

    /**
     * @brief: 读取uint16_t
     * @param {number} offset: 偏移字节数
     */
    ReadU16(offset) {
        if(offset === void 0)
            offset = 0;

        let read_len = 2;
        if(offset + read_len > this._size)
            throw("out of range");

        return this._dv.getUint16(offset, this._endian);
    }

    /**
     * @brief: 读取uint32_t
     * @param {number} offset: 偏移字节数
     */
    ReadU32(offset) {
        if(offset === void 0)
            offset = 0;

        let read_len = 4;
        if(offset + read_len > this._size)
            throw("out of range");

        return this._dv.getUint32(offset, this._endian);
    }

        /**
     * @brief: 读取int8_t
     * @param {number} offset: 偏移字节数
     */
    ReadI8(offset) {
        if(offset === void 0)
            offset = 0;

        let read_len = 1;
        if(offset + read_len > this._size)
            throw("out of range");

        return this._dv.getInt8(offset);
    }

    /**
     * @brief: 读取int16_t
     * @param {number} offset: 偏移字节数
     */
    ReadI16(offset) {
        if(offset === void 0)
            offset = 0;

        let read_len = 2;
        if(offset + read_len > this._size)
            throw("out of range");

        return this._dv.getInt16(offset, this._endian);
    }

    /**
     * @brief: 读取uint32_t
     * @param {number} offset: 偏移字节数
     */
    ReadI32(offset) {
        if(offset === void 0)
            offset = 0;

        let read_len = 4;
        if(offset + read_len > this._size)
            throw("out of range");

        return this._dv.getInt32(offset, this._endian);
    }

    /**
     * @brief: 写入一个u8类型数据
     * @param {number} u8: 
     * @param {number} offset: 
     */
    WriteU8(u8, offset) {
        if(offset === void 0)
            offset = 0;

        let write_len = 1;
        if(offset + write_len > this._size)
            throw("out of range");

        this._dv.setUint8(offset, u8, this._endian);
    }

    /**
     * @brief: 写入一个u16类型数据
     * @param {number} u16: 
     * @param {number} offset: 
     */
    WriteU16(u16, offset) {
        if(offset === void 0)
            offset = 0;

        let write_len = 2;
        if(offset + write_len > this._size)
            throw("out of range");

        this._dv.setUint16(offset, u16, this._endian);
    }

    /**
     * @brief: 写入一个u32类型数据
     * @param {number} u32: 
     * @param {number} offset: 
     */
    WriteU32(u32, offset) {
        if(offset === void 0)
            offset = 0;

        let write_len = 4;
        if(offset + write_len > this._size)
            throw("out of range");

        this._dv.setUint32(offset, u32, this._endian);
    }


    /**
     * @brief: 写入一个i8类型数据
     * @param {number} u8: 
     * @param {number} offset: 
     */
    WriteI8(i8, offset) {
        if(offset === void 0)
            offset = 0;

        let write_len = 1;
        if(offset + write_len > this._size)
            throw("out of range");

        this._dv.setInt8(offset, i8, this._endian);
    }

    /**
     * @brief: 写入一个i16类型数据
     * @param {number} i16: 
     * @param {number} offset: 
     */
    WriteI16(i16, offset) {
        if(offset === void 0)
            offset = 0;

        let write_len = 2;
        if(offset + write_len > this._size)
            throw("out of range");

        this._dv.setInt16(offset, i16, this._endian);
    }

    /**
     * @brief: 写入一个i32类型数据
     * @param {number} i32: 
     * @param {number} offset: 
     */
    WriteI32(i32, offset) {
        if(offset === void 0)
            offset = 0;

        let write_len = 4;
        if(offset + write_len > this._size)
            throw("out of range");

        this._dv.setInt32(offset, i32, this._endian);
    }

    /**
     * @brief: 读取一个ArrayBuffer
     * @param {number} byte_count 
     */
    ReadArrayBuffer(byte_count, offset) {
        if(offset === void 0)
            offset = 0;
        if(byte_count + offset > this._size)
            throw("out of range");

        let arr_buff = new ArrayBuffer(byte_count);
        xlib.ArrayBuffer_DataCopy(this._arrbuff, offset, offset + byte_count, arr_buff);

        return arr_buff;
    }

    /**
     * @brief: 写入一个ArrayBuffer
     * @param {ArrayBuffer} arr_buff:  
     * @param {number} offser:      
     */
    WriteArrayBuffer(arr_buff, offset) {    
        if(offset === void 0)
            offset = 0;

        let write_len = 4;
        if(offset + write_len > this._size)
            throw("out of range");

        xlib.ArrayBuffer_DataCopy(arr_buff, 0, arr_buff.byteLength, this._arrbuff, offset);
    }
}

/**
 * @brief: 单元测试代码
 */
ByteBuffer.UnitTest = function() {
    let buffer = new xlib.ByteBuffer();
    buffer.Resize((1 + 2 + 4) * 2 * 2);
    let write_offset = 0;
    buffer.WriteI8(xlib.MAX_INT8, write_offset);
    write_offset += 1;
    buffer.WriteI8(xlib.MIN_INT8, write_offset);
    write_offset += 1;
    buffer.WriteI16(xlib.MAX_INT16, write_offset);
    write_offset += 2;
    buffer.WriteI16(xlib.MIN_INT16, write_offset);
    write_offset += 2;
    buffer.WriteI32(xlib.MAX_INT32, write_offset);
    write_offset += 4;
    buffer.WriteI32(xlib.MIN_INT32, write_offset);
    write_offset += 4;
    buffer.WriteU8(xlib.MAX_UINT8, write_offset);
    write_offset += 1;
    buffer.WriteU8(xlib.MIN_UINT8, write_offset);
    write_offset += 1;
    buffer.WriteU16(xlib.MAX_UINT16, write_offset);
    write_offset += 2;
    buffer.WriteU16(xlib.MIN_UINT16, write_offset);
    write_offset += 2;
    buffer.WriteU32(xlib.MAX_UINT32, write_offset);
    write_offset += 4;
    buffer.WriteU32(xlib.MIN_UINT32, write_offset);
    write_offset += 4;

    let read_offset = 0;
    xlib.assert(xlib.MAX_INT8 == buffer.ReadI8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MIN_INT8 == buffer.ReadI8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MAX_INT16 == buffer.ReadI16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MIN_INT16 == buffer.ReadI16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MAX_INT32 == buffer.ReadI32(read_offset));
    read_offset +=4;
    xlib.assert(xlib.MIN_INT32 == buffer.ReadI32(read_offset));
    read_offset +=4;
    xlib.assert(xlib.MAX_UINT8 == buffer.ReadU8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MIN_UINT8 == buffer.ReadU8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MAX_UINT16 == buffer.ReadU16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MIN_UINT16 == buffer.ReadU16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MAX_UINT32 == buffer.ReadU32(read_offset));
    read_offset +=4;
    xlib.assert(xlib.MIN_UINT32 == buffer.ReadU32(read_offset));
    read_offset +=4;

    buffer.Resize(515);
    read_offset = 0;
    xlib.assert(xlib.MAX_INT8 == buffer.ReadI8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MIN_INT8 == buffer.ReadI8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MAX_INT16 == buffer.ReadI16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MIN_INT16 == buffer.ReadI16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MAX_INT32 == buffer.ReadI32(read_offset));
    read_offset +=4;
    xlib.assert(xlib.MIN_INT32 == buffer.ReadI32(read_offset));
    read_offset +=4;
    xlib.assert(xlib.MAX_UINT8 == buffer.ReadU8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MIN_UINT8 == buffer.ReadU8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MAX_UINT16 == buffer.ReadU16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MIN_UINT16 == buffer.ReadU16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MAX_UINT32 == buffer.ReadU32(read_offset));
    read_offset +=4;
    xlib.assert(xlib.MIN_UINT32 == buffer.ReadU32(read_offset));
    read_offset +=4;

    buffer.Resize(6);
    read_offset = 0;
    xlib.assert(xlib.MAX_INT8 == buffer.ReadI8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MIN_INT8 == buffer.ReadI8(read_offset));
    read_offset +=1;
    xlib.assert(xlib.MAX_INT16 == buffer.ReadI16(read_offset));
    read_offset +=2;
    xlib.assert(xlib.MIN_INT16 == buffer.ReadI16(read_offset));
    read_offset +=2;
};

// 导出到xlib名字空间
xlib.ByteBuffer = ByteBuffer;

if(xlib.__enable_auto_unit_test)
    ByteBuffer.UnitTest();